using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Linq;
using System.Web.Security;
using Jaws.Core.Web.Entities;
using Jaws.Core.Web.Entities.Users;

namespace Jaws.Core.Web.Providers
{
    /// <summary>
    /// 
    /// </summary>
    public class XmlMembershipProvider : MembershipProvider
    {
        UserCollection users = XmlCollectionBase<UserCollection, User>.Load();

        private readonly Encoding _encoder = Encoding.UTF8;
        private string _applicationName;
        private int _attempts;
        private int _attemptWindow;
        private bool _enableReset;
        private bool _enableRetrieve;
        private MembershipPasswordFormat _format;
        private int _minNonAlphaChars;
        private int _minPasswordLength;
        private string _passwordExpression;
        private bool _requiresChallenge;
        private bool _uniqueEmail;

        /// <summary>
        /// The name of the application using the custom membership provider.
        /// </summary>
        /// <value></value>
        /// <returns>The name of the application using the custom membership provider.</returns>
        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        /// <summary>
        /// Indicates whether the membership provider is configured to allow users to reset their passwords.
        /// </summary>
        /// <value></value>
        /// <returns>true if the membership provider supports password reset; otherwise, false. The default is true.</returns>
        public override bool EnablePasswordReset
        {
            get { return _enableReset; }
        }

        /// <summary>
        /// Indicates whether the membership provider is configured to allow users to retrieve their passwords.
        /// </summary>
        /// <value></value>
        /// <returns>true if the membership provider is configured to support password retrieval; otherwise, false. The default is false.</returns>
        public override bool EnablePasswordRetrieval
        {
            get { return _enableRetrieve; }
        }

        /// <summary>
        /// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out.
        /// </summary>
        /// <value></value>
        /// <returns>The number of invalid password or password-answer attempts allowed before the membership user is locked out.</returns>
        public override int MaxInvalidPasswordAttempts
        {
            get { return _attempts; }
        }


        /// <summary>
        /// Gets the minimum number of special characters that must be present in a valid password.
        /// </summary>
        /// <value></value>
        /// <returns>The minimum number of special characters that must be present in a valid password.</returns>
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return _minNonAlphaChars; }
        }


        /// <summary>
        /// Gets the minimum length required for a password.
        /// </summary>
        /// <value></value>
        /// <returns>The minimum length required for a password. </returns>
        public override int MinRequiredPasswordLength
        {
            get { return _minPasswordLength; }
        }

        /// <summary>
        /// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.
        /// </summary>
        /// <value></value>
        /// <returns>The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.</returns>
        public override int PasswordAttemptWindow
        {
            get { return _attemptWindow; }
        }

        /// <summary>
        /// Gets a value indicating the format for storing passwords in the membership data store.
        /// </summary>
        /// <value></value>
        /// <returns>One of the <see cref="T:System.Web.Security.MembershipPasswordFormat"></see> values indicating the format for storing passwords in the data store.</returns>
        public override MembershipPasswordFormat PasswordFormat
        {
            get { return _format; }
        }

        /// <summary>
        /// Gets the regular expression used to evaluate a password.
        /// </summary>
        /// <value></value>
        /// <returns>A regular expression used to evaluate a password.</returns>
        public override string PasswordStrengthRegularExpression
        {
            get { return _passwordExpression; }
        }

        /// <summary>
        /// Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval.
        /// </summary>
        /// <value></value>
        /// <returns>true if a password answer is required for password reset and retrieval; otherwise, false. The default is true.</returns>
        public override bool RequiresQuestionAndAnswer
        {
            get { return _requiresChallenge; }
        }


        /// <summary>
        /// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name.
        /// </summary>
        /// <value></value>
        /// <returns>true if the membership provider requires a unique e-mail address; otherwise, false. The default is true.</returns>
        public override bool RequiresUniqueEmail
        {
            get { return _uniqueEmail; }
        }

        private UserCollection RetrieveUsers(bool activeOnly)
        {
            
            if (activeOnly)
            {
                return users.FindAllActiveUsers();
            }
            return users;
        }

        private static byte[] ComputeHash(byte[] input)
        {
            byte[] hash;
            using (var provider = new SHA512Managed())
            {
                hash = provider.ComputeHash(input);
                provider.Clear();
            }

            return hash;
        }

        private static MembershipUserCollection CreateMembershipUserCollection(IList<User> users)
        {
            var coll = new MembershipUserCollection();
            foreach (User usr in users)
            {
                coll.Add(usr.CreateMembershipUser(Membership.Provider.Name));
            }

            return coll;
        }

        private bool CompareByteArray(byte[] array1, byte[] array2)
        {
            string string1 = _encoder.GetString(array1);
            string string2 = _encoder.GetString(array2);

            return String.Compare(string1, string2) == 0;
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The friendly name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        /// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
        /// <exception cref="T:System.InvalidOperationException">An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"></see> on a provider after the provider has already been initialized.</exception>
        /// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (String.IsNullOrEmpty(name))
            {
                name = "XmlMembershipProvider";
            }

            base.Initialize(name, config);

            if (!String.IsNullOrEmpty(config["passwordFormat"]))
            {
                _format =
                    (MembershipPasswordFormat)
                    Enum.Parse(typeof(MembershipPasswordFormat), config["passwordFormat"], true);
            }
            if (!String.IsNullOrEmpty(config["applicationName"]))
            {
                _applicationName = config["applicationName"];
            }
            if (!String.IsNullOrEmpty(config["enablePasswordRetrieval"]))
            {
                _enableRetrieve = bool.Parse(config["enablePasswordRetrieval"]);
            }
            if (!String.IsNullOrEmpty(config["enablePasswordReset"]))
            {
                _enableReset = bool.Parse(config["enablePasswordReset"]);
            }
            if (!String.IsNullOrEmpty(config["requiresQuestionAndAnswer"]))
            {
                _requiresChallenge = bool.Parse(config["requiresQuestionAndAnswer"]);
            }
            if (!String.IsNullOrEmpty(config["requiresUniqueEmail"]))
            {
                _uniqueEmail = bool.Parse(config["requiresUniqueEmail"]);
            }
            if (!String.IsNullOrEmpty(config["passwordAttemptWindow"]))
            {
                _attemptWindow = int.Parse(config["passwordAttemptWindow"], CultureInfo.InvariantCulture);
            }
            if (!String.IsNullOrEmpty(config["passwordStrengthRegularExpression"]))
            {
                _passwordExpression = config["passwordStrengthRegularExpression"];
            }
            if (!String.IsNullOrEmpty(config["maxInvalidPasswordAttempts"]))
            {
                _attempts = int.Parse(config["maxInvalidPasswordAttempts"], CultureInfo.InvariantCulture);
            }
            if (!String.IsNullOrEmpty(config["minRequiredNonAlphanumericCharacters"]))
            {
                _minNonAlphaChars = int.Parse(config["minRequiredNonAlphanumericCharacters"],
                                              CultureInfo.InvariantCulture);
            }
            if (!String.IsNullOrEmpty(config["minRequiredPasswordLength"]))
            {
                _minPasswordLength = int.Parse(config["minRequiredPasswordLength"], CultureInfo.InvariantCulture);
            }
        }

        /// <summary>
        /// Processes a request to update the password for a membership user.
        /// </summary>
        /// <param name="username">The user to update the password for.</param>
        /// <param name="oldPassword">The current password for the specified user.</param>
        /// <param name="newPassword">The new password for the specified user.</param>
        /// <returns>
        /// true if the password was updated successfully; otherwise, false.
        /// </returns>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            UserCollection users = RetrieveUsers(true);
            User usr = users.FindByUserName(username);

            byte[] oldPass = _encoder.GetBytes(oldPassword);
            byte[] newPass = _encoder.GetBytes(newPassword);

            if (usr != null)
            {
                switch (PasswordFormat)
                {
                    case MembershipPasswordFormat.Clear:
                        if (CompareByteArray(usr.Password, oldPass))
                        {
                            usr.Password = newPass;
                        }
                        else
                        {
                            return false;
                        }
                        break;
                    case MembershipPasswordFormat.Encrypted:
                        if (CompareByteArray(usr.Password, base.EncryptPassword(oldPass)))
                        {
                            usr.Password = base.EncryptPassword(newPass);
                        }
                        else
                        {
                            return false;
                        }
                        break;
                    case MembershipPasswordFormat.Hashed:
                        if (CompareByteArray(usr.Password, ComputeHash(oldPass)))
                        {
                            usr.Password = ComputeHash(newPass);
                        }
                        else
                        {
                            return false;
                        }
                        break;
                }

                users.Save();
                return true;
            }
            return false;
        }

        /// <summary>
        /// Processes a request to update the password question and answer for a membership user.
        /// </summary>
        /// <param name="username">The user to change the password question and answer for.</param>
        /// <param name="password">The password for the specified user.</param>
        /// <param name="newPasswordQuestion">The new password question for the specified user.</param>
        /// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
        /// <returns>
        /// true if the password question and answer are updated successfully; otherwise, false.
        /// </returns>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password,
                                                             string newPasswordQuestion, string newPasswordAnswer)
        {
            UserCollection users = RetrieveUsers(true);
            User usr = users.FindByUserName(username);

            byte[] passBytes = _encoder.GetBytes(password);

            if (usr != null)
            {
                switch (PasswordFormat)
                {
                    case MembershipPasswordFormat.Clear:
                        if (!CompareByteArray(usr.Password, passBytes))
                        {
                            return false;
                        }
                        break;
                    case MembershipPasswordFormat.Encrypted:
                        if (!CompareByteArray(usr.Password, base.EncryptPassword(passBytes)))
                        {
                            return false;
                        }
                        break;
                    case MembershipPasswordFormat.Hashed:
                        if (!CompareByteArray(usr.Password, ComputeHash(passBytes)))
                        {
                            return false;
                        }
                        break;
                }

                usr.ChallengeQuestion = newPasswordQuestion;
                usr.ChallengeAnswer = newPasswordAnswer;
                users.Save();

                return true;
            }
            return false;
        }

        /// <summary>
        /// Adds a new membership user to the data source.
        /// </summary>
        /// <param name="username">The user name for the new user.</param>
        /// <param name="password">The password for the new user.</param>
        /// <param name="email">The e-mail address for the new user.</param>
        /// <param name="passwordQuestion">The password question for the new user.</param>
        /// <param name="passwordAnswer">The password answer for the new user</param>
        /// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
        /// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
        /// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
        /// </returns>
        public override MembershipUser CreateUser(string username, string password, string email,
                                                  string passwordQuestion, string passwordAnswer, bool isApproved,
                                                  object providerUserKey, out MembershipCreateStatus status)
        {
            UserCollection users = RetrieveUsers(false);

            //Perform Checks
            if (users.FindByUserName(username) != null)
            {
                status = MembershipCreateStatus.DuplicateUserName;
                return null;
            }
            if (RequiresUniqueEmail && users.FindByEmail(email) != null)
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            var newUser = new User();

            byte[] pass = _encoder.GetBytes(password);

            newUser.Key = Guid.NewGuid();
            newUser.UserName = username;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    newUser.Password = pass;
                    break;
                case MembershipPasswordFormat.Hashed:
                    newUser.Password = ComputeHash(pass);
                    break;
                case MembershipPasswordFormat.Encrypted:
                    newUser.Password = base.EncryptPassword(pass);
                    break;
            }

            newUser.CreatedOn = DateTime.UtcNow;
            newUser.LastLoggedIn = DateTime.UtcNow;
            newUser.LastActivity = DateTime.UtcNow;
            newUser.IsActive = isApproved;
            newUser.Email = email;
            newUser.ChallengeQuestion = passwordQuestion;
            newUser.ChallengeAnswer = passwordAnswer;

            users.Add(newUser);
            users.Save();

            status = MembershipCreateStatus.Success;
            return newUser.CreateMembershipUser(Membership.Provider.Name);
        }

        /// <summary>
        /// Removes a user from the membership data source.
        /// </summary>
        /// <param name="username">The name of the user to delete.</param>
        /// <param name="deleteAllRelatedData">true to delete data related to the user from the database; false to leave data related to the user in the database.</param>
        /// <returns>
        /// true if the user was successfully deleted; otherwise, false.
        /// </returns>
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(username);

            if (usr != null)
            {
                if (deleteAllRelatedData)
                {
                    users.Remove(usr);
                }
                else
                {
                    usr.IsActive = false;
                }

                users.Save();
                return true;
            }
            return false;
        }

        /// <summary>
        /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match.
        /// </summary>
        /// <param name="emailToMatch">The e-mail address to search for.</param>
        /// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">The total number of matched users.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
        /// </returns>
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize,
                                                                  out int totalRecords)
        {
            UserCollection users = RetrieveUsers(false);

            var coll = new List<User>();

            foreach (User u in users)
            {
                if (string.Compare(emailToMatch, u.Email, true, CultureInfo.CurrentCulture) == 0)
                {
                    coll.Add(u);
                }
            }

            totalRecords = coll.Count;

            return
                CreateMembershipUserCollection(coll.GetRange(pageIndex * pageSize,
                                                             (totalRecords < pageSize ? totalRecords : pageSize)));
        }

        /// <summary>
        /// Gets a collection of membership users where the user name contains the specified user name to match.
        /// </summary>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">The total number of matched users.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
        /// </returns>
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize,
                                                                 out int totalRecords)
        {
            UserCollection users = RetrieveUsers(false);

            var coll = new List<User>();

            foreach (User u in users)
            {
                if (string.Compare(usernameToMatch, u.UserName, true, CultureInfo.CurrentCulture) == 0)
                {
                    coll.Add(u);
                }
            }

            totalRecords = coll.Count;

            return
                CreateMembershipUserCollection(coll.GetRange(pageIndex * pageSize,
                                                             (totalRecords < pageSize ? totalRecords : pageSize)));
        }

        /// <summary>
        /// Gets a collection of all the users in the data source in pages of data.
        /// </summary>
        /// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">The total number of matched users.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
        /// </returns>
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            UserCollection users = RetrieveUsers(false);
            totalRecords = users.Count;

            var userList = new List<User>(users);
            return
                CreateMembershipUserCollection(userList.GetRange(pageIndex * pageSize,
                                                                 (totalRecords < pageSize ? totalRecords : pageSize)));
        }

        /// <summary>
        /// Gets the number of users currently accessing the application.
        /// </summary>
        /// <returns>
        /// The number of users currently accessing the application.
        /// </returns>
        public override int GetNumberOfUsersOnline()
        {
            int onlineCounter = 0;
            int minutes = Membership.UserIsOnlineTimeWindow;

            UserCollection users = RetrieveUsers(true);
            foreach (User u in users)
            {
                if (u.LastActivity > DateTime.UtcNow.AddMinutes(-minutes))
                {
                    onlineCounter++;
                }
            }

            return onlineCounter;
        }

        /// <summary>
        /// Gets the password for the specified user name from the data source.
        /// </summary>
        /// <param name="username">The user to retrieve the password for.</param>
        /// <param name="answer">The password answer for the user.</param>
        /// <returns>
        /// The password for the specified user name.
        /// </returns>
        public override string GetPassword(string username, string answer)
        {
            UserCollection users = RetrieveUsers(true);
            User usr = users.FindByUserName(username);

            if (usr != null && string.Compare(usr.ChallengeAnswer, answer, true, CultureInfo.CurrentCulture) == 0)
            {
                switch (PasswordFormat)
                {
                    case MembershipPasswordFormat.Clear:
                        return _encoder.GetString(usr.Password);
                    case MembershipPasswordFormat.Encrypted:
                        return _encoder.GetString(base.DecryptPassword(usr.Password));
                }
            }

            throw new NotSupportedException();
        }

        /// <summary>
        /// Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user.
        /// </summary>
        /// <param name="username">The name of the user to get information for.</param>
        /// <param name="userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the specified user's information from the data source.
        /// </returns>
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(username);

            if (usr != null)
            {
                return usr.CreateMembershipUser(Membership.Provider.Name);
            }

            return null;
        }

        /// <summary>
        /// Gets user information from the data source based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user.
        /// </summary>
        /// <param name="providerUserKey">The unique identifier for the membership user to get information for.</param>
        /// <param name="userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the specified user's information from the data source.
        /// </returns>
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            var key = (Guid)providerUserKey;

            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByKey(key);

            if (usr != null)
            {
                if (userIsOnline)
                {
                    usr.LastActivity = DateTime.UtcNow;
                    users.Save();
                }

                return usr.CreateMembershipUser(Membership.Provider.Name);
            }

            return null;
        }

        /// <summary>
        /// Gets the user name associated with the specified e-mail address.
        /// </summary>
        /// <param name="email">The e-mail address to search for.</param>
        /// <returns>
        /// The user name associated with the specified e-mail address. If no match is found, return null.
        /// </returns>
        public override string GetUserNameByEmail(string email)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByEmail(email);

            if (usr != null)
            {
                return usr.UserName;
            }

            return null;
        }

        /// <summary>
        /// Resets a user's password to a new, automatically generated password.
        /// </summary>
        /// <param name="username">The user to reset the password for.</param>
        /// <param name="answer">The password answer for the specified user.</param>
        /// <returns>The new password for the specified user.</returns>
        public override string ResetPassword(string username, string answer)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(username);

            string password = Membership.GeneratePassword(_minPasswordLength, _minNonAlphaChars);
            byte[] newPassword = _encoder.GetBytes(password);

            if (usr != null)
            {
                switch (PasswordFormat)
                {
                    case MembershipPasswordFormat.Clear:
                        usr.Password = newPassword;
                        break;
                    case MembershipPasswordFormat.Encrypted:
                        usr.Password = base.EncryptPassword(newPassword);
                        break;
                    case MembershipPasswordFormat.Hashed:
                        usr.Password = ComputeHash(newPassword);
                        break;
                }

                users.Save();
                return password;
            }

            return null;
        }

        /// <summary>
        /// Clears a lock so that the membership user can be validated.
        /// </summary>
        /// <param name="userName">The membership user whose lock status you want to clear.</param>
        /// <returns>
        /// true if the membership user was successfully unlocked; otherwise, false.
        /// </returns>
        public override bool UnlockUser(string userName)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(userName);

            if (usr != null)
            {
                usr.IsLocked = false;
                users.Save();
            }

            return true;
        }

        /// <summary>
        /// Updates information about a user in the data source.
        /// </summary>
        /// <param name="user">A <see cref="T:System.Web.Security.MembershipUser"></see> object that represents the user to update and the updated information for the user.</param>
        public override void UpdateUser(MembershipUser user)
        {
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }

            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(user.UserName);

            if (usr != null)
            {
                usr.LastActivity = user.LastActivityDate;
                usr.LastLockedOut = user.LastLockoutDate;
                usr.LastPasswordChange = user.LastPasswordChangedDate;
                usr.ChallengeQuestion = user.PasswordQuestion;
                usr.Email = user.Email;
                usr.IsLocked = user.IsLockedOut;
                usr.IsActive = user.IsApproved;
                usr.Description = user.Comment;

                users.Save();
            }
        }


        /// <summary>
        /// Verifies that the specified user name and password exist in the data source.
        /// </summary>
        /// <param name="username">The name of the user to validate.</param>
        /// <param name="password">The password for the specified user.</param>
        /// <returns>
        /// true if the specified username and password are valid; otherwise, false.
        /// </returns>
        public override bool ValidateUser(string username, string password)
        {
            UserCollection users = RetrieveUsers(false);
            User usr = users.FindByUserName(username);

            byte[] passwordBytes = _encoder.GetBytes(password);
            bool passed = false;

            if (usr != null)
            {
                switch (PasswordFormat)
                {
                    case MembershipPasswordFormat.Clear:
                        passed = CompareByteArray(usr.Password, passwordBytes);
                        break;
                    case MembershipPasswordFormat.Encrypted:
                        passed = CompareByteArray(usr.Password, DecryptPassword(passwordBytes));
                        break;
                    case MembershipPasswordFormat.Hashed:
                        passed = CompareByteArray(usr.Password, ComputeHash(passwordBytes));
                        break;
                }
            }

            if (passed)
            {
                if (usr.IsLocked)
                {
                    passed = false;
                }
                else
                {
                    usr.LastLoggedIn = DateTime.UtcNow;
                    usr.LastActivity = DateTime.UtcNow;
                    users.Save();
                }
            }

            return passed;
        }
    }
}